﻿using Autodesk.Revit.DB;
using CoreDB.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace CoreDB.Extensions
{
    /// <summary>
    /// 虽然我们处理的大多数Curve都是Line，但是我们依然认为Curve应当包含Line，所以我们将所有Curve其他类型泛用的方法放到CurveExtension中
    /// </summary>
    public static class CurveExtension
    {
        private const double defaultEps = 1e-3;
        /// <summary>
        /// 判断Curve是否有效
        /// </summary>
        /// <param name="curve"></param>
        /// <param name="minLength"></param>
        /// <returns></returns>
        /// <remarks>It isn't always accurate judgment, because sometime don't care the length.</remarks>
        public static bool IsValid(this Curve curve, double minLength = 0.0027)
        {
            if (curve is null)
                throw new ArgumentNullException(nameof(curve));

            if (curve.IsBound && curve.Length < minLength)
                return false;

            return true;
        }
        /// <summary>
        /// 线段和直线都有Start
        /// </summary>
        /// <param name="line"></param>
        /// <returns></returns>
        public static XYZ Start(this Curve curve)
        {
            if (curve.IsBound)
                return curve.GetEndPoint(0);
            else
                return null;
        }
        /// <summary>
        /// 线段必须闭合才会有End点
        /// </summary>
        /// <param name="line"></param>
        /// <returns></returns>
        public static XYZ End(this Curve curve)
        {
            if (curve.IsBound)
                return curve.GetEndPoint(1);
            else
                return null;
        }
        /// <summary>
        /// 找到所有的点
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        public static List<XYZ> Vertices(this Curve c)
        {
            return c.Tessellate().ToList();
        }
        /// <summary>
        /// 找到线段的起点终点
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        public static List<XYZ> EndPoints(this Curve c)
        {
            List<XYZ> result = new List<XYZ>();
            if (c.IsBound)
            {
                result.Add(c.GetEndPoint(0));
                result.Add(c.GetEndPoint(1));
            }
            return result;
        }
        /// <summary>
        /// 得到线的中点。
        /// </summary>
        /// <param name="line">指定线段</param>
        /// <returns>线段中点</returns>
        public static XYZ Center(this Curve curve)
        {
            return curve.Evaluate(0.5, true);
        }
        /// <summary>
        /// 得到线的平均点
        /// 重心其实不是这样求的，但是Revit本身细分的平均点基本也接近重心，所以暂时可以这样做
        /// </summary>
        /// <param name="curve"></param>
        /// <returns></returns>
        public static XYZ Centroid(this Curve curve)
        {
            return curve.Vertices().AveragePoint();
        }
        /// <summary>
        /// 检查给定的点事否在线上，可以忽略端点
        /// </summary>
        /// <param name="baseCurve">The case curve to check.</param>
        /// <param name="point">The point to check.</param>
        /// <param name="excludeEnds">True：包含端点 </param>
        /// <param name="eps">The compare tolearance.</param>
        /// <returns></returns>
        public static bool IsPointOnCurve(this Curve baseCurve, XYZ point, bool excludeEnds = true, double eps = 1E-4)
        {
            if (null == baseCurve || null == point)
                return false;
            try
            {
                double distance = baseCurve.Distance(point);
                // Check the distance is zero or not
                if (distance.IsEqual(0.0, eps))
                {
                    if (excludeEnds && baseCurve.IsBound &&
                        (point.IsEqual(baseCurve.GetEndPoint(0), eps)
                        || point.IsEqual(baseCurve.GetEndPoint(1), eps)))
                        // if the given point is end point, return false.
                        return false;
                }
                else
                    return false;
            }
            catch (Autodesk.Revit.Exceptions.InvalidOperationException)
            {
                return false;
            }
            return true;
        }
        /// <summary>
        /// Curve 的矩阵变换
        /// </summary>
        /// <param name="curve"></param>
        /// <param name="transform"></param>
        /// <param name="inverse"></param>
        /// <returns></returns>
        public static Curve OfTrans(this Curve curve, Transform transform, bool inverse = false)
        {
            if (!curve.IsValid())
                throw new InvalidDataException(nameof(curve));

            if (transform is null)
                throw new ArgumentNullException(nameof(transform));

            if (inverse)
                transform = transform.Inverse;

            return curve.CreateTransformed(transform);
        }
    }
    public static class CurvesExtension
    {
        private const double defaultEps = 1e-3;
        /// <summary>
        /// 中心
        /// </summary>
        /// <param name="curves"></param>
        /// <param name="distinctPoints"></param>
        /// <returns></returns>
        public static XYZ Center(this IEnumerable<Curve> curves)
        {
            #region 以线段中心为基础，套用质心公式，与质心算法过于接近，暂时屏蔽
            //XYZ curvesCenterSum = new XYZ();
            //double lengthSum = 0.0;
            //foreach (Curve curve in curves)
            //{
            //    curvesCenterSum += curve.Center() * curve.ApproximateLength;
            //    lengthSum += curve.ApproximateLength;
            //}
            //if (lengthSum.IsZero() || curvesCenterSum.IsEqual(XYZ.Zero))
            //    return XYZ.Zero;
            //return curvesCenterSum / lengthSum;
            #endregion

            // 求线段的中心的平均点
            return curves.Select(x => x.Center()).AveragePoint();
        }
        /// <summary>
        /// 质心
        /// </summary>
        /// <param name="curves"></param>
        /// <param name="distinctPoints"></param>
        /// <returns></returns>
        public static XYZ Centroid(this IEnumerable<Curve> curves)
        {
            XYZ curvesCentroidSum = new XYZ();
            double lengthSum = 0.0;
            foreach (Curve curve in curves)
            {
                curvesCentroidSum += curve.Centroid() * curve.ApproximateLength;
                lengthSum += curve.ApproximateLength;
            }
            if (lengthSum.IsZero() || curvesCentroidSum.IsEqual(XYZ.Zero))
                return XYZ.Zero;

            return curvesCentroidSum / lengthSum;
        }

        /// <summary>
        /// 一串Curve可能是首尾相连的，也可能是不闭合的
        /// 找出他们中自由点，即端点，数量不明
        /// </summary>
        /// <param name="curves"></param>
        /// <param name="eps"></param>
        public static List<XYZ> GetFreePts(this IEnumerable<Curve> curves, double eps = defaultEps)
        {
            List<TempPoint> tempPts = new List<TempPoint>();
            foreach (Curve c in curves)
            {
                AddPoint(c.Start());
                AddPoint(c.End());
            }
            return tempPts.FindAll(x => x.UsedTimes == 1).Select(x => x.Coord).ToList();
            void AddPoint(XYZ point)
            {
                var repeatPt = tempPts.Find(x => x.Coord.IsEqual(point, eps));
                if (repeatPt != null)
                    repeatPt.UsedTimes++;
                else
                    tempPts.Add(new TempPoint(point));
            }
        }



        public static List<Curve> Clone(this IEnumerable<Curve> curves)
        {
            return curves.Select(x => x.Clone()).ToList();
        }
    }


    public class CurveComparer : IEqualityComparer<Curve>
    {
        /// <summary>
        /// 是否忽略每条线的Z坐标值
        /// </summary>
        public bool IgnoreZ { get; set; }

        /// <summary>
        /// 是否将完全重合的线段认定为相同线段.
        /// 如果为False，如果首尾相反
        /// </summary>
        public bool IgnoreDir { get; set; }

        public double Eps { get; set; }

        public CurveComparer(bool ignoreZ = false, bool ignoreDir = false, double eps = 0.001)
        {
            IgnoreZ = ignoreZ;
            IgnoreDir = ignoreDir;
            Eps = eps;
        }
        public bool Equals(Curve a, Curve b)
        {
            //Line x = (a as Line);
            //Line y = (b as Line);
            //if (IgnoreZ && IgnoreDir)
            //    return x.FlatCoincide(y, Eps);
            //if (IgnoreZ)
            //    return x.FlatEqual(y, Eps);
            //if (IgnoreDir)
            //    return x.IsCoincide(y, Eps);
            //return x.IsEqual(y, Eps);
            return true;
        }

        public int GetHashCode(Curve obj)
        {
            // 始终指明相等，让处理相等逻辑继续交给Equals。
            return 0;
        }
    }

}
